home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 6984 / 6984.xpi / chrome / lazarus.jar / content / sqlite.js < prev    next >
Text File  |  2009-11-24  |  12KB  |  361 lines

  1.  
  2.  
  3. /**
  4. * SQLite object for mozilla
  5.  
  6. == example usage ==
  7.  
  8. var db = new SQLite("%profile%/test.sqlite");
  9.  
  10. var rs = db.rs("SELECT * FROM USERS WHERE id = ?1", 124);
  11.  
  12. if (!db.error){
  13.     for (var i=0; i<rs.length; i++){
  14.         ...do something with recordset
  15.     }
  16. }
  17. else {
  18.     ...show error message
  19. }
  20.  
  21.  
  22. */
  23.  
  24. //http://textsnippets.com/posts/show/1030#related
  25. Lazarus.SQLite = function(filepath, useSharedCache){
  26.     
  27.     var _this = this;
  28.     
  29.     _this.filepath = filepath;
  30.     
  31.     //are we allowed multiple connetcions to this database?
  32.     _this.useSharedCache = (typeof useSharedCache == "undefined") ? true : useSharedCache;
  33.     
  34.     //mozIStorageService
  35.     _this.storageService = Components.classes["@mozilla.org/storage/service;1"].getService(Components.interfaces.mozIStorageService);
  36.     
  37.     //database connection
  38.     _this.conn = null;
  39.     
  40.     //any error object will be placed here.
  41.     _this.lastError = null;
  42.     
  43.     //most recent SQL query
  44.     _this.lastQuery = null;
  45.     
  46.     //array of argument to be passed to the sql query
  47.     _this.lastQueryArgs = null;
  48.     
  49.     /**
  50.     * opens a connection to the specified SQLite database 
  51.     */
  52.     _this.connect = function(){
  53.         _this.error = null;
  54.         
  55.         if (!_this.conn){
  56.             try {
  57.                 //allow for files to be relative to the profile directory
  58.                 if (_this.filepath.indexOf("%profile%") == 0){
  59.                     var dir = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile).path;
  60.                     _this.filepath = _this.filepath.replace("%profile%", dir);
  61.                     if (dir.indexOf("/") > -1){
  62.                         _this.filepath = _this.filepath.replace(/\\/g, "/");
  63.                     }
  64.                     else {
  65.                         _this.filepath = _this.filepath.replace(/\//g, "\\");
  66.                     }   
  67.                 }
  68.                 
  69.                 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
  70.                 file.initWithPath(_this.filepath);
  71.                 if (_this.useSharedCache){
  72.                     _this.conn = _this.storageService. openDatabase(file);
  73.                 }
  74.                 else {
  75.                     _this.conn = _this.storageService. openUnsharedDatabase(file);
  76.                 }
  77.             }
  78.             catch(err){
  79.                 //add some better info to the error message
  80.                 throw Error("SQLite: failed to open database\n'"+ _this.filepath +"'\n"+ err.message);
  81.             }
  82.         }
  83.     }
  84.     
  85.     /**
  86.     * close the connection to the database (this doesn't appear to work)
  87.     */
  88.     _this.close = function(){
  89.         _this.conn = null;
  90.     }
  91.     
  92.     /**
  93.     * sends an error to the error console
  94.     * but allows code to contnue
  95.     */
  96.     _this.logError = function(err){
  97.     
  98.         if (typeof err == "string"){
  99.             //generate a better error message
  100.             err = new Error("SQLite Error: "+ err);
  101.         }
  102.         
  103.         //add some more info to the error
  104.         if (_this.lastQuery){
  105.             //WTF: cannot append data to the err.message!
  106.             //err.message += "\nSQL = '"+ _this.lastQuery.query +"'";
  107.             //we'll throw an error now with the needed info
  108.             Components.utils.reportError("SQLite.lastQuery = '"+ _this.lastQuery.query +"'");
  109.         }
  110.         _this.lastError = err;
  111.     }
  112.     
  113.     /**
  114.     * Adds some SQL specific info and then throws the error 
  115.     */
  116.     _this.throwError = function(err){
  117.         _this.logError(err);
  118.         throw _this.lastError;
  119.     }
  120.     
  121.     /**
  122.     * return the final formatted query
  123.     */
  124.     _this.getQuery = function(queryObj){
  125.         var q = queryObj.query;
  126.         for (var i=0; i<queryObj.args.length; i++){
  127.             q = q.replace(/\?\d/, "'"+ queryObj.args[i].toString().replace(/'/, "\\'") +"'");
  128.         }
  129.         return q;
  130.     }
  131.     
  132.     /**
  133.     * executes an sql query against the current database
  134.     */
  135.     _this.runQuery = function(queryObj){
  136.     
  137.         //pass a string (+ replacements args)
  138.         if (typeof queryObj == "string"){
  139.             queryObj = _this.queryFromArguments(arguments);
  140.         }
  141.         //or an arguments object that contains a string (+ replacements args)
  142.         else if (typeof queryObj.length == "number"){
  143.             queryObj = _this.queryFromArguments(queryObj);
  144.         }
  145.     
  146.         _this.lastQuery = queryObj;
  147.         _this.lastError = null;
  148.         
  149.         _this.connect();
  150.         //make copies of the query and args 
  151.         //so we can add them to error messages
  152.         
  153.         var defer = false;
  154.         
  155.         //use deferred transaction if database is currently busy
  156.         if (_this.conn.transactionInProgress){
  157.             defer = true;
  158.             _this.conn.beginTransactionAs(_this.conn.TRANSACTION_DEFERRED);
  159.         }
  160.         
  161.         //build the statement
  162.         try {
  163.             var statement = _this.conn.createStatement(queryObj.query);
  164.             if (queryObj.args){
  165.                 for (var i=0; i<queryObj.args.length; i++){
  166.                     var arg = queryObj.args[i].toString();
  167.                     statement.bindUTF8StringParameter(i, arg);
  168.                 }
  169.             }
  170.         
  171.             //SELECT statements use executeStep()
  172.             //but all others use execute.
  173.             //no we can use executeStep() even on a NON select statement.
  174.             // ref: http://developer.mozilla.org/en/docs/Storage    
  175.             var dataset = [];
  176.             var columns = null;
  177.             while (statement.executeStep()){
  178.                 var row = {};
  179.                 //we need to calculate the types of each column
  180.                 //do not use statement.columnCount in the for loop, fetches the value again and again
  181.                 var cols = statement.columnCount;
  182.                 if (columns === null){
  183.                     columns = [];
  184.                     for (var i=0; i<cols; i++){
  185.                         columns[i] = {
  186.                             "type": statement.getTypeOfIndex(i), //0=null, 1=int, 2=float, 3=string, 4=blob
  187.                             "name": statement.getColumnName(i)
  188.                         };
  189.                     }
  190.                 }
  191.                 
  192.                 //keep a copy of the columns
  193.                 _this.lastQuery.columns = columns;
  194.                 
  195.                 for (var i=0; i<cols; i++){
  196.                     var val;
  197.                     switch (columns[i].type){
  198.                         case 0: //null
  199.                             val = null;
  200.                             break;
  201.                         case 1: //int
  202.                             val = statement.getInt64(i);
  203.                             break;
  204.                         case 2: //float
  205.                             val = statement.getDouble(i);
  206.                             break
  207.                         case 3: //string
  208.                             val = statement.getUTF8String(i);
  209.                             break;
  210.                         case 4: //blob
  211.                         default:
  212.                             _this.logError("Unable to handle datatype "+ columns[i].type);
  213.                             val = null;                            
  214.                     }
  215.                     row[columns[i].name] = val;
  216.                 }
  217.                 
  218.                 dataset.push(row);
  219.             }
  220.         }
  221.         catch(err){
  222.             _this.logError(err);
  223.         }
  224.         finally {
  225.             //must make sure statement is reset
  226.             statement.reset();
  227.         }
  228.         
  229.         //commit delayed transaction
  230.         if (defer){
  231.             _this.conn.commitTransaction();
  232.         }
  233.         
  234.         //if there was an error we should throw it now that the statement has been reset
  235.         if (_this.lastError){
  236.             throw _this.lastError;
  237.         }
  238.         //otherwise return any dataset
  239.         return dataset;
  240.     }
  241.     
  242.     /**
  243.     * convert an arguments object into a query object
  244.     */
  245.     _this.queryFromArguments = function(argObj){
  246.         
  247.         var query = {
  248.             "query": argObj[0],
  249.             "args" : []
  250.         }
  251.         for (var i=1; i<argObj.length; i++){
  252.             query.args.push(argObj[i]);
  253.         }
  254.         return query;        
  255.     }
  256.     
  257.     /**
  258.     * executes a command (INSERT, UPDATE, CREATE, etc..) against the current database
  259.     */
  260.     _this.exe = function(query, arg1 /*, arg2, arg3...*/){
  261.         _this.runQuery(arguments);
  262.         return true;
  263.     }
  264.     
  265.     /**
  266.     * execute an INSERT statement and return the last_insert_rowid 
  267.     */
  268.     _this.insert = function(){
  269.         _this.runQuery("BEGIN TRANSACTION");
  270.         _this.runQuery(arguments);
  271.         var id = _this.getInt("SELECT last_insert_rowid()");
  272.         _this.runQuery("COMMIT TRANSACTION");
  273.         return id;
  274.     }
  275.     
  276.     /**
  277.     * runs a SELECT statement and returns a recordset 
  278.     * will return an empty array if no matches are found  
  279.     */
  280.     _this.rs = function(query, arg1 /*, arg2, arg3...*/){
  281.         return _this.runQuery(arguments);
  282.     }
  283.     
  284.     /**
  285.     * return a single column of results as an array
  286.     */
  287.     _this.getColumn = function(query, arg1 /*, arg2, arg3...*/){
  288.         var rs = _this.runQuery(arguments);
  289.         var column = [];
  290.         for (var i=0; i<rs.length; i++){
  291.             for(var key in rs[i]){
  292.                 if (typeof column[i] == "undefined"){
  293.                     column[i] = rs[i][key];
  294.                 }
  295.                 else {
  296.                     _this.throwError("Too many columns, getColumn expects the dataset to contain 1 and only 1 column");
  297.                 }
  298.             }
  299.         }
  300.         return column;
  301.     }
  302.     
  303.     /**
  304.     * return a single row of results as an associate array (js object)
  305.     */
  306.     _this.getRow = function(query, arg1 /*, arg2, arg3...*/){
  307.         var rs = _this.runQuery(arguments);
  308.         return (rs[0]) ? rs[0] : null;
  309.     }
  310.     
  311.     
  312.     /**
  313.     * returns a single INTEGER result from a query.
  314.     * NOTE: the function expects the query to return a single value, if the query returns a more then one row
  315.     * or more than one column the we will throw an error   
  316.     */
  317.     _this.getInt = function(query, arg1 /*, arg2, arg3...*/){
  318.         var rs = _this.runQuery(arguments);
  319.         if (rs.length == 0){
  320.             //no results 
  321.             return 0;
  322.         }
  323.         else {
  324.             for(var col in rs[0]){
  325.                 var val = parseInt(rs[0][col]);
  326.                 return (val === NaN) ? 0 : val;
  327.             }
  328.         }
  329.         //should get here
  330.         return null;
  331.     }
  332.     
  333.     /**
  334.     * returns a single STRING result from a query.
  335.     * NOTE: the function expects the query to return a single value, if the query returns a more then one row
  336.     * or more than one column the we will throw an error   
  337.     */
  338.     _this.getStr = function(query, arg1 /*, arg2, arg3...*/){
  339.         var rs = _this.runQuery(arguments);
  340.         if (rs.length == 0){
  341.             //no results 
  342.             return '';
  343.         }
  344.         else {
  345.             for(var col in rs[0]){
  346.                 return rs[0][col];
  347.             }
  348.         }
  349.         //should not get here
  350.         return null;
  351.     }
  352.     
  353.     /**
  354.     * return TRUE if table exists in the current database
  355.     */
  356.     _this.tableExists = function(name){
  357.         return (_this.getInt("SELECT count(*) FROM sqlite_master WHERE name = ?1", name) > 0) ? true : false;
  358.     }
  359. }
  360.  
  361.